package com.yonyou.pmclouds.device.rmiimpl;

import com.alibaba.dubbo.config.annotation.Service;
import com.yonyou.pmclouds.ApplicationConsts;
import com.yonyou.pmclouds.device.DeviceInfoMaintain;
import com.yonyou.pmclouds.device.entity.DeviceInfoVO;
import com.yonyou.pmclouds.device.mapper.DeviceInfoMapper;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

@Service(version = ApplicationConsts.APPLICATION_VERSION, interfaceClass = DeviceInfoMaintain.class)
public class DeviceInfoMaintainImpl implements DeviceInfoMaintain {

    // TODO 其实这里的数据存储使用其他的非关系型数据库或许会有更好的效果

    private static final String DEVICE_BLOOM_FILTER = "device_bloom_filter";
    private static final long EXCEPT_DEVICE_NUM = 5000000;
    private RBloomFilter<Object> bloomFilter;
    @Autowired
    private RedissonClient client;
    @Autowired
    private DeviceInfoMapper mapper;

    @PostConstruct
    public void initBloomFilter() {
        bloomFilter = client.getBloomFilter(DEVICE_BLOOM_FILTER);
        bloomFilter.tryInit(EXCEPT_DEVICE_NUM, 0.01);
    }

    @Override
    public void insertDeviceInfos(List<DeviceInfoVO> infoVOs) {
        if (infoVOs == null || infoVOs.isEmpty()) {
            return;
        }
        // 查询已有info，考虑缓存
        Iterator<DeviceInfoVO> it = infoVOs.iterator();
        Map<String, DeviceInfoVO> infoVOMap = new HashMap<>();
        while (it.hasNext()) {
            DeviceInfoVO info = it.next();
            // 使用布隆过滤器，降低数据库的压力
            // TODO 先注释掉，布隆过滤器的效果有点奇怪，误判很高
//            if (bloomFilter.contains(genDeviceKey(info))) {
//                it.remove();
//                continue;
//            }
            infoVOMap.put(genDeviceKey(info), info);

        }
        if (infoVOs.isEmpty()) {
            return;
        }
        // 这里说明一下，布隆过滤器有误判率，如果出现透穿数据库比较厉害的情况，可能需要考虑调整参数
        List<DeviceInfoVO> savedDevices = mapper.queryDeviceInfos(infoVOs);
        // 去掉已经保存的数据
        for (DeviceInfoVO device : savedDevices) {
            infoVOMap.remove(genDeviceKey(device));
        }
        if (!infoVOMap.isEmpty()) {
            // 插入新的info
            mapper.saveInfos(infoVOMap.values());
        }
        // 向布隆过滤器里添加数据
        for (DeviceInfoVO device : infoVOs) {
            bloomFilter.add(genDeviceKey(device));
        }
    }

    private String genDeviceKey(DeviceInfoVO info) {
        return info.getDeviceId() + info.getUserId();
    }
}
